/////////////////////////////////////////////////////////////////////
//
//  MOE Framework (Meta Object Encapsulation Framework)
//
//  Original Author: Wiksy Wang, 2011
//
/////////////////////////////////////////////////////////////////////

#include "Namespace.h"

#include "IObjectToNameQuery.h"

namespace MOE
{

namespace Name
{

Namespace& Namespace::GetGlobalNamespace()
{
	static Namespace globalNamespace;
	return globalNamespace;
}

const RawByteVector& Namespace::GetObjectName(void* object)
{
	if (gObjectToName)
	{
		return gObjectToName->GetObjectName(object);
	}
	else
	{
		return RawByteVector::gEmptyStr;
	}
}

boost::shared_ptr<IObjectToNameQuery> Namespace::gObjectToName;

Namespace::Namespace(Namespace* parent, const RawByteVector& name)
	: _parent(parent)
	, _name(name)
	, _managedObjects()
{
}

Namespace::~Namespace()
{
	RemoveAll();
}

Address Namespace::GetObject(const NamePath& namePath, ClassIDType typeID) const
{
	return GetObjectImpl(namePath, typeID);
}

bool Namespace::SetObject(const NamePath& namePath, void* object, ClassIDType typeID)
{
	return SetObjectImpl(namePath, object, typeID);
}

bool Namespace::RemoveObject(const NamePath& namePath)
{
	return RemoveObjectImpl(namePath);
}

Namespace* Namespace::GetChildNamespace(const NamePath& namePath)
{
	return GetChildNamespaceImpl(namePath);
}

const Namespace* Namespace::GetChildNamespace(const NamePath& namePath) const
{
	return GetChildNamespaceImpl(namePath);
}

Namespace* Namespace::CreateChildNamespace(const NamePath& namePath)
{
	return CreateChildNamespaceImpl(namePath);
}

bool Namespace::RemoveChildNamespace(const NamePath& namePath)
{
	return RemoveChildNamespaceImpl(namePath);
}

void Namespace::RemoveAll()
{
	struct RemoveAllVisitor
	{
		void OnObject(const RawByteVector& /*name*/, const ClassIDType /*typeId*/, void* const object)
		{
			// Do nothing for child objects. The namespace is not responsible for the release of such objects.

			// Remove entry from Object-to-Name query
			if (gObjectToName)
			{
				gObjectToName->RemoveObject(object);
			}
		}

		void OnNamespace(const RawByteVector& name, Namespace* const childSpace)
		{
			// Iteratively call RemoveAll on child namespaces.
			childSpace->RemoveAll();
			delete childSpace;
		}
	};

	Visit(RemoveAllVisitor());
	_managedObjects.clear();
}

RawByteVector Namespace::GetRelativePath(const NamePath& fullPath) const
{
	size_t depth = 0;
	for (const Namespace* space = this; space->_parent != NULL; space = space->_parent, ++depth);
	return static_cast<const RawByteVector&>(fullPath.GetSubPath(depth));
}

RawByteVector Namespace::GetFullPathImpl(const unsigned int delimiter, const size_t charTypeSize) const
{
	if (!_parent)
	{
		return _name;
	}
	else
	{
		// Get parent path.
		RawByteVector parentFullPath = _parent->GetFullPathImpl(delimiter, charTypeSize);

		// Append delimiter and current path.
		const size_t parentPathLength = parentFullPath.size();
		if (parentPathLength > 0)
		{
			parentFullPath.resize(parentPathLength + charTypeSize + _name.size());
			memcpy(parentFullPath.data() + parentPathLength, &delimiter, charTypeSize);
			memcpy(parentFullPath.data() + parentPathLength + charTypeSize, _name.data(), _name.size());

			return parentFullPath;
		}
		else
		{
			return _name;
		}
	}
}

RawByteVector Namespace::GetChildFullPathImpl(const NamePathRef& namePath) const
{
	const unsigned int delimiter = namePath.GetDelimiter();
	const size_t charSize = namePath.GetCharSize();
	RawByteVector namespaceFullPath = GetFullPathImpl(delimiter, charSize);
	RawByteVectorRef childString = namePath.GetPath();	// Need this to be alive to keep childStringRef valid.
	const RawByteVector& childStringRef = childString;

	// Append delimiter and child path.
	const size_t namespacePathLength = namespaceFullPath.size();
	if (namespacePathLength > 0)
	{
		namespaceFullPath.resize(namespacePathLength + charSize + childStringRef.size());
		memcpy(namespaceFullPath.data() + namespacePathLength, &delimiter, charSize);
		memcpy(namespaceFullPath.data() + namespacePathLength + charSize, childStringRef.data(), childStringRef.size());

		return namespaceFullPath;
	}
	else
	{
		return childStringRef;
	}
}

Address Namespace::GetObjectImpl(const NamePathRef& namePath, ClassIDType typeID) const
{
	ChildMap::const_iterator iter = _managedObjects.find(namePath.GetFirstSegment());
	if (iter == _managedObjects.end())
	{
		return NULL;
	}

	if (namePath.GetSplitCount() == 0)
	{
		// What we want to find is an object. Check if it's not a namespace.
		if (iter->second.IsNamespace())
		{
			return NULL;
		}

		// TODO: Add type checks

		// Return the object if it's of the right type.
		return iter->second._pointer;
	}
	else
	{
		// We must go deeper into this child namespace.
		if (!iter->second.IsNamespace())
		{
			return NULL;
		}

		const Namespace* childSpace = static_cast<Namespace*>(iter->second._pointer);
		return childSpace->GetObjectImpl(NamePathRef(namePath, 1), typeID);
	}
}

bool Namespace::SetObjectImpl(const NamePathRef& namePath, void* object, ClassIDType typeID)
{
	// For path access, create the object in the child namespace specified by the path.
	// New namespaces will be created along the way.
	if (namePath.GetSplitCount() > 0)
	{
		Namespace* childNamespace = CreateChildNamespaceImpl(NamePathRef(namePath, 0, 0));
		return childNamespace->SetObjectImpl(NamePathRef(namePath, 1), object, typeID);
	}
	
	// If namePath doesn't include a path, then this is the namespace the object should live in.
	ChildMap::iterator iter = _managedObjects.find(namePath.GetPath());
	if (iter != _managedObjects.end())
	{
		// We need first to make sure the name doesn't collide with a namespace.
		if (iter->second.IsNamespace())
		{
			return false;
		}
		else
		{
			// And if it's not, then replace the object.

			if (gObjectToName)
			{
				gObjectToName->RemoveObject(iter->second._pointer);
				gObjectToName->AddObject(object, GetChildFullPathImpl(namePath));
			}

			// Note that we are safe to remove non-namespace objects, as we are not responsible for
			// their memory management here.
			iter->second = ChildEntry(typeID, object);
		}
	}
	else
	{
		// Now it's safe to add the object.
		if (gObjectToName)
		{
			gObjectToName->AddObject(object, GetChildFullPathImpl(namePath));
		}
		_managedObjects[namePath.GetPath()] = ChildEntry(typeID, object);
	}

	return true;
}

bool Namespace::RemoveObjectImpl(const NamePathRef& namePath)
{
	ChildMap::iterator iter = _managedObjects.find(namePath.GetFirstSegment());
	if (iter == _managedObjects.end())
	{
		return false;
	}

	// If namePath specifies an object in current namespace, then try to remove that.
	// If it specifies a path, then try to go into child namespaces.
	if (namePath.GetSplitCount() == 0)
	{
		if (iter->second.IsNamespace())
		{
			return false;
		}
		else
		{
			if (gObjectToName)
			{
				gObjectToName->RemoveObject(iter->second._pointer);
			}
			_managedObjects.erase(iter);
			return true;
		}
	}
	else
	{
		if (iter->second.IsNamespace())
		{
			return RemoveObjectImpl(NamePathRef(namePath, 1));
		}
		else
		{
			return false;
		}
	}
}

Namespace* Namespace::GetChildNamespaceImpl(const NamePathRef& namePath)
{
	return const_cast<Namespace*>(const_cast<const Namespace*>(this)->GetChildNamespaceImpl(namePath));
}

const Namespace* Namespace::GetChildNamespaceImpl(const NamePathRef& namePath) const
{
	ChildMap::const_iterator iter = _managedObjects.find(namePath.GetFirstSegment());
	// Do IsNamespace check here, because we need a namespace either way.
	if (iter == _managedObjects.end() || !iter->second.IsNamespace())
	{
		return NULL;
	}

	const Namespace* childNamespace = static_cast<const Namespace*>(iter->second._pointer);
	if (namePath.GetSplitCount() == 0)
	{
		return childNamespace;
	}
	else
	{
		return childNamespace->GetChildNamespaceImpl(NamePathRef(namePath, 1));
	}
}

Namespace* Namespace::CreateChildNamespaceImpl(const NamePathRef& namePath)
{
	ChildMap::iterator iter = _managedObjects.find(namePath.GetFirstSegment());
	if (iter != _managedObjects.end())
	{
		// If the path name collides with an object, then we fail.
		if (!iter->second.IsNamespace())
		{
			return NULL;
		}

		// Otherwise, either return the child namespace or iteratively create grandchild namespace.
		Namespace* childNamespace = static_cast<Namespace*>(iter->second._pointer);
		if (namePath.GetSplitCount() == 0)
		{
			return childNamespace;
		}
		else
		{
			return childNamespace->CreateChildNamespaceImpl(NamePathRef(namePath, 1));
		}
	}
	else
	{
		// Since it's not found, we can directly create child namespace.
		Namespace* childNamespace = new Namespace(this, namePath.GetFirstSegment());
		_managedObjects[namePath.GetFirstSegment()] = ChildEntry(childNamespace);
		
		if (namePath.GetSplitCount() == 0)
		{
			return childNamespace;
		}
		else
		{
			return childNamespace->CreateChildNamespaceImpl(NamePathRef(namePath, 1));
		}
	}
}

bool Namespace::RemoveChildNamespaceImpl(const NamePathRef& namePath)
{
	ChildMap::iterator iter = _managedObjects.find(namePath.GetFirstSegment());
	// Do IsNamespace check here, because we need a namespace either way.
	if (iter == _managedObjects.end() || !iter->second.IsNamespace())
	{
		return false;
	}

	// If namePath specifies a namespace in current namespace, then try to remove that.
	// If it specifies a path, then try to go into child namespaces.
	if (namePath.GetSplitCount() == 0)
	{
		Namespace* childNamespace = static_cast<Namespace*>(iter->second._pointer);
		delete childNamespace;
		_managedObjects.erase(iter);
		return true;
	}
	else
	{
		return RemoveObjectImpl(NamePathRef(namePath, 1));
	}
}

}

}
